/**
 ** 通用控制单元，用于将指令解析成控制信号
 ** 具体控制信号生成规则，参考文档
**/

module commonControl (
    input wire [31:0] now_inst,     //当前的指令内容
    input wire [31:0] pc,           //当前的指令地址
    output wire rob_write_en,       //寄存器写信号
    output reg [1:0] rob_write_sel,     //寄存器选择信号
    output wire alu_a_sel,          //运算单元的输入选择信号
    output wire alu_b_sel,
    output reg [2:0] work_mode,     //工作模式信号
    output reg [3:0] alu_ctrl,      //运算器的控制信号
    output reg [3:0] complex_ctrl,  //乘除法的控制信号
    output reg [2:0] dm_rd_ctrl,    //数据存储器的读取控制信号
    output reg [1:0] dm_wr_ctrl,    //数据存储器的写入控制信号
    output wire rob_save_direct_ctrl,//直接将内容写入ROB的控制信号
    output reg rd_en, rt_en, rs_en,  //是否有rd, rt, rs
    output reg [2:0] imm_ctrl       //立即数拓展控制信号
);

    //得到指令中的某些部分内容，方便处理
    wire [6:0] optcode;
    wire [2:0] f3;
    wire [6:0] f7;
    wire complex_mode;

    assign optcode = now_inst[6:0];
    assign f3 = now_inst[14:12];
    assign f7 = now_inst[31:25];
    //乘法和除法的低位操作码是一样的，区别在于f3的最高位
    assign complex_mode = now_inst[14];

    //定义特定指令的前缀，方便比较
    wire [6:0] RI; //R型指令的操作码 0110011
    wire [6:0] II; //I型指令的操作码 0010011
    wire [6:0] LI; //L型指令的操作码 0000011
    wire [6:0] SI; //S型指令的操作码 0100011
    wire [6:0] BI; //B型指令的操作码 1100011
    wire [6:0] LUI; //U型指令，操作码 0110111
    wire [6:0] AUIPC; //U型指令，操作码 0010111
    wire [6:0] JAL; //J型指令，操作码 1101111
    wire [6:0] JALR; //J型指令，操作码 1100111
    wire [6:0] LOADI; // 加载立即数到寄存器的伪指令，原始RISC中无此指令

    assign RI = 7'b0110011;
    assign II = 7'b0010011;
    assign LI = 7'b0000011;
    assign SI = 7'b0100011;
    assign BI = 7'b1100011;
    assign LUI = 7'b0110111;
    assign AUIPC = 7'b0010111;
    assign JAL = 7'b1101111;
    assign JALR = 7'b1100111;
    assign LOADI = 7'b0000001;

    //特定指令的信号
    //R
    wire r_opt; //R型指令前缀
    wire is_add;
    wire is_sub;
    wire is_and;
    wire is_xor;
    wire is_or;
    wire is_sll;
    wire is_srl;
    wire is_sra;
    wire is_slt;
    wire is_sltu;
    wire is_r_type;
    //I
    wire i_opt; //i型指令前缀
    wire is_addi;
    wire is_andi;
    wire is_xori;
    wire is_ori;
    wire is_slli;
    wire is_srli;
    wire is_srai;
    wire is_slti;
    wire is_sltiu;
    wire is_i_type;
    //L
    wire l_opt; //l型指令前缀
    wire is_lb;
    wire is_lh;
    wire is_lw;
    wire is_lbu;
    wire is_lhu;
    wire is_l_type;
    //S
    wire s_opt; //s型指令前缀
    wire is_sb;
    wire is_sh;
    wire is_sw;
    wire is_s_type;
    //B
    wire b_opt; //b型指令前缀
    wire is_beq;
    wire is_bne;
    wire is_blt;
    wire is_bge;
    wire is_bltu;
    wire is_bgeu;
    wire is_b_type;
    //U
    wire u_opt; //R型指令前缀
    wire is_lui;
    wire is_auipc;
    wire is_u_type;
    //J
    wire is_jal;
    wire is_jalr;
    wire is_j_type;
    //乘除法运算
    wire is_complex;
    wire is_mul;
    wire is_div;
    wire is_alu_type;
    //加载立即数
    wire is_loadI;

    //使用门电路确定指令的具体作用
    //R型指令
    assign r_opt = optcode==RI;
    assign is_add = r_opt && f3==3'b000 && f7==7'b0000000;
    assign is_sub = r_opt && f3==3'b000 && f7==7'b0100000;
	assign is_and = r_opt && f3==3'b111 && f7==7'b0000000;
	assign is_xor = r_opt && f3==3'b100 && f7==7'b0000000;
	assign is_or = r_opt && f3==3'b110 && f7==7'b0000000;
	assign is_sll = r_opt && f3==3'b001 && f7==7'b0000000;
	assign is_srl = r_opt && f3==3'b101 && f7==7'b0000000;
	assign is_sra = r_opt && f3==3'b101 && f7==7'b0100000;
	assign is_slt = r_opt && f3==3'b010 && f7==7'b0000000;
	assign is_sltu = r_opt && f3==3'b011 && f7==7'b0000000;
    assign is_mul = r_opt && complex_mode==0;
    assign is_div = r_opt && complex_mode==1;
    //I型指令
    assign i_opt = optcode==II;
    assign is_addi = i_opt && f3==3'b000;
	assign is_andi = i_opt && f3==3'b111;
	assign is_xori = i_opt && f3==3'b100;
	assign is_ori = i_opt && f3==3'b110;
	assign is_slli = i_opt && f3==3'b001 && f7==7'b0000000;
	assign is_srli = i_opt && f3==3'b101 && f7==7'b0000000;
	assign is_srai = i_opt && f3==3'b101 && f7==7'b0100000;
	assign is_slti = i_opt && f3==3'b010;
	assign is_sltiu = i_opt && f3==3'b011;
    //L型指令
    assign l_opt = optcode==LI;
    assign is_lb = l_opt && f3==3'b000;
	assign is_lh = l_opt && f3==3'b001;
	assign is_lw = l_opt && f3==3'b010;
	assign is_lbu = l_opt && f3==3'b100;
	assign is_lhu = l_opt && f3==3'b101;
    //S型指令
    assign s_opt = optcode==SI;
    assign is_sb = s_opt && f3==3'b000;
    assign is_sh = s_opt && f3==3'b001;
    assign is_sw = s_opt && f3==3'b010;
    //B型指令
    assign b_opt = optcode==BI;
    assign is_beq = b_opt && f3==3'b000;
    assign is_bne = b_opt && f3==3'b001;
    assign is_blt = b_opt && f3==3'b100;
    assign is_bge = b_opt && f3==3'b101;
    assign is_bltu = b_opt && f3==3'b110;
    assign is_bgeu = b_opt && f3==3'b111;
    //U型指令
    assign is_lui = optcode==LUI;
	assign is_auipc = optcode==AUIPC;
    //J型指令
    assign is_jal = optcode==JAL;
    assign is_jalr = optcode==JALR;
    //LOADI指令
    assign is_loadI = optcode==LOADI;

    assign  is_u_type = is_lui | is_auipc ;
    assign  is_b_type = is_beq | is_bne | is_blt | is_bge | is_bltu | is_bgeu ;
    assign  is_r_type = is_add | is_sub | is_sll | is_slt | is_sltu | is_xor | is_srl | is_sra | is_or | is_and | is_mul | is_div;
    assign  is_alu_type = is_add | is_sub | is_sll | is_slt | is_sltu | is_xor | is_srl | is_sra | is_or | is_and;
    assign  is_i_type = is_addi | is_slti | is_sltiu | is_xori | is_ori | is_andi | is_slli | is_srli | is_srai;
    assign  is_s_type = is_sb | is_sh | is_sw;
    assign  is_l_type = is_lb | is_lh | is_lw | is_lbu | is_lhu;
    assign  is_j_type = is_jal | is_jalr;

    assign is_alu_i = is_addi | is_slti | is_sltiu | is_xori | is_ori | is_andi | is_slli | is_srli | is_srai;

    //由于这里引入了ROB，所以这里的寄存器写入信号可以认为是ROB的写入使能信号
    assign rob_write_en = is_u_type | is_j_type | is_i_type | is_r_type | is_loadI;

    //要向寄存器中写入的选择信号，不同类型的指令向寄存器中写入的内容有差异
    always@(*) begin
        if(is_j_type)
            rob_write_sel = 2'b01;
        else if(is_alu_i || is_r_type || is_u_type)
            rob_write_sel = 2'b10;
        else if(is_l_type || is_u_type)
            rob_write_sel = 2'b11;
        else
            rob_write_sel = 2'b00;
    end 

    //alu_ctrl
    assign  alu_type_1 = is_auipc | is_j_type | is_s_type | is_l_type | is_add | is_addi;
    
    always@(*) begin
        if(alu_type_1)
            alu_ctrl = 4'b0000;
        else if(is_sub || is_b_type)
            alu_ctrl = 4'b1000;
        else if(is_sll || is_slli)
            alu_ctrl = 4'b0001;
        else if(is_srl || is_srli)
            alu_ctrl = 4'b0101;
        else if(is_sra || is_srai)
            alu_ctrl = 4'b1101;
        else if(is_slt || is_slti)
            alu_ctrl = 4'b0010;
        else if(is_sltu || is_sltiu)
            alu_ctrl = 4'b0011;
        else if(is_xor || is_xori)
            alu_ctrl = 4'b0100;
        else if(is_or || is_ori)
            alu_ctrl = 4'b0110;
        else if(is_and || is_andi)
            alu_ctrl = 4'b0111;
        else if(is_lui)
            alu_ctrl = 4'b1110;
        else
            alu_ctrl = 4'b1111;
    end

    //work_mode
    always @(*) begin
        if (is_alu_type || is_lui) begin
            work_mode = 3'b000;          //普通算数运算
        end
        else if (is_mul) begin
            work_mode = 3'b001;          //整数乘法
        end
        else if (is_div) begin
            work_mode = 3'b010;          //整数除法
        end
        else if (is_i_type) begin
            work_mode = 3'b011;          //立即数算术运算
        end
        else if (is_l_type) begin
            work_mode = 3'b100;          //LOAD指令
        end
        else if(is_s_type) begin
            work_mode = 3'b101;          //STORE指令
        end
        else if (is_b_type) begin
            work_mode = 3'b110;          //跳转指令
        end
        else begin
            work_mode = 3'b111;
        end
    end

    //给乘法or除法分配控制信号，输出信号为全1则表示不做操作
    always @(*) begin
        if (is_mul || is_div) begin
            complex_ctrl = {1'b0, f3};
        end
        else begin
            complex_ctrl = 4'b1111;
        end
    end

    //ROB直接写入信号
    assign rob_save_direct_ctrl = is_loadI;

    //alu_a_sel
    assign alu_a_sel = (is_r_type || is_i_type || is_jalr || is_l_type || is_s_type) ? 1 : 0;

    //alu_b_sel
    assign alu_b_sel = is_r_type ? 0 : 1;

    //[2:0]dm_rd_ctrl
    always@(*)
    begin
        if(is_lb)
            dm_rd_ctrl = 3'b001;
        else if(is_lbu)
            dm_rd_ctrl = 3'b010;
        else if(is_lh)
            dm_rd_ctrl = 3'b011;
        else if(is_lhu)
            dm_rd_ctrl = 3'b100;
        else if(is_lw)
            dm_rd_ctrl = 3'b101;
        else
            dm_rd_ctrl = 3'b000;
    end

    //[1:0]dm_wr_ctrl
    always@(*)
    begin
        if(is_sb)
            dm_wr_ctrl = 2'b01;
        else if(is_sh)
            dm_wr_ctrl = 2'b10;
        else if(is_sw)
            dm_wr_ctrl = 2'b11;
        else
            dm_wr_ctrl = 2'b00;
    end

    //立即数控制信号
    always @(*) begin
        if(is_auipc || is_lui) begin
            imm_ctrl = 3'b001;
        end
        else if(is_jal) begin
            imm_ctrl = 3'b010;
        end
        else if(is_b_type) begin
            imm_ctrl = 3'b011;
        end
        else if(is_i_type || is_l_type || is_jalr) begin
            imm_ctrl = 3'b101;
        end
        else if(is_s_type) begin
            imm_ctrl = 3'b110;
        end
        else if(is_loadI) begin
            imm_ctrl = 3'b111;
        end
        else begin
            imm_ctrl = 3'b000;
        end
    end

    //是否存在rd寄存器
    always @(*) begin
        if (is_s_type || is_b_type) begin
            rd_en = 0;
        end
        else if (is_r_type || is_i_type || is_l_type || is_u_type || is_j_type || is_loadI || is_complex) begin
            rd_en = 1;
        end
        else begin
            rd_en = 0;
        end
    end

    //是否有rt寄存器
    always @(*) begin
        if (is_l_type || is_j_type || is_i_type || is_loadI || is_u_type ||is_j_type) begin
            rt_en = 0;
        end
        else if (is_r_type || is_s_type || is_b_type) begin
            rt_en = 1;
        end
        else begin
            rt_en = 0;
        end
    end

    //是否有rs寄存器
    always @(*) begin
        if (is_r_type || is_i_type || is_b_type || is_s_type || is_l_type || is_jalr) begin
            rs_en = 1;
        end
        else if (is_u_type || is_jal || is_loadI) begin
            rs_en = 0;
        end
        else begin
            rs_en = 0;
        end
    end
endmodule